package org.tool.reflect;

import org.tool.TipException;
import org.tool.core.lang.Assert;
import org.tool.reflect.annotations.Primary;

import java.lang.reflect.Constructor;
import java.util.*;

/**
 * 有关构造函数的工具类
 *
 * @author 李坤
 * @date 2022/4/20 22:32
 */
public abstract class ConstructorUtil {

    /**
     * 基本类型的默认值集合
     */
    private static final Map<Class<?>, Object> DEFAULT_TYPE_VALUES;

    static {
        Map<Class<?>, Object> values = new HashMap<>();

        values.put(int.class, 0);
        values.put(long.class, 0L);
        values.put(float.class, 0F);
        values.put(double.class, 0D);
        values.put(char.class, '\0');
        values.put(boolean.class, false);
        values.put(byte.class, (byte) 0);
        values.put(short.class, (short) 0);

        DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
    }

    public static <T> Constructor<T> getMatchedConstructor(Class<T> clazz, Object[] args) {
        Assert.notNull(clazz, "clazz must not be null");

        return getMatchedConstructor((Constructor<T>[]) clazz.getDeclaredConstructors(), args);
    }

    /**
     * 创建给定类型的对象实例
     *
     * @param clazz 类型
     * @param <T>   泛型类型
     *
     * @return 对象实例
     */
    public static <T> T newInstance(Class<T> clazz) {
        return newInstance(clazz, false);
    }

    /**
     * 创建给定类型的对象实例
     * <p>
     * 注：如果不存在与给定参数匹配的构造函数，则抛出异常
     *
     * @param clazz 类型
     * @param args  构造函数参数
     * @param <T>   泛型类型
     *
     * @return 对象实例
     */
    public static <T> T newInstance(Class<T> clazz, Object[] args) {
        return newInstance(clazz, false, args);
    }

    /**
     * 创建给定类型的对象实例
     * <p>
     * 注：如果 isStrictCheck == true，且不存在与给定参数匹配的构造函数，则抛出异常
     *
     * @param clazz         类型
     * @param isStrictCheck 是有严格校验构造函数参数
     * @param args          构造函数参数
     * @param <T>           泛型类型
     *
     * @return 对象实例
     */
    public static <T> T newInstance(Class<T> clazz, boolean isStrictCheck, Object... args) {
        Assert.notNull(clazz, "待生成对象的类型不能为空");

        Constructor<T>[] constructors;

        if ((constructors = (Constructor<T>[]) clazz.getDeclaredConstructors()).length != 0) {
            // 获取最合适的构造函数
            Constructor<T> constructor = getMatchedConstructor(constructors, args);

            // 实例化构造函数
            if (!isStrictCheck || Objects.nonNull(constructor)) {
                // 如果给定的参数不存在对应的构造函数，则使用默认构造函数
                if (Objects.isNull(constructor)) {
                    constructor = constructors[0];
                    args = ConstructorConstant.EMPTY_OBJECT_ARRAY;
                }
                // 实例化构造函数
                return instanceConstructor(constructor, args);
            }
        }
        throw new TipException("待生成对象的类型没有可用的构造函数");
    }

    /*---------------------------------------------private static methods---------------------------------------------*/

    /**
     * 根据给定的参数类型数组，实例化的构造函数
     *
     * @param constructor 构造函数
     * @param args        参数数组
     * @param <T>         泛型类型
     *
     * @return 实例化的对象
     */
    private static <T> T instanceConstructor(Constructor<T> constructor, Object... args) {
        // 获取构造函数的参数个数
        int parameterCount = constructor.getParameterCount();

        // 如果构造函数参数为空，则使用默认值
        if (args.length == 0 && parameterCount > 0) {
            args = new Object[parameterCount];
            Class<?>[] parameterTypes = constructor.getParameterTypes();

            // 遍历设置实例化构造函数的参数值
            for (int index = 0; index < parameterCount; index++) {
                Class<?> parameterType = parameterTypes[index];
                args[index] = parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null;
            }
        }

        // 如果构造函数参数个数不匹配，则抛出异常
        if (args.length != parameterCount) {
            throw new TipException("构造函数参数个数不匹配");
        }

        try {
            // 实例化构造函数
            return constructor.newInstance(args);
        } catch (Exception exception) {
            final String argsStr = Arrays.toString(args);
            throw new TipException(String.format("实例化构造函数失败；%s，%s", constructor, argsStr));
        }
    }

    /**
     * 从给定的构造函数中选择匹配的构造函数
     *
     * @param constructors 构造函数数组
     * @param args         参数数组
     * @param <T>          泛型类型
     *
     * @return 最简单的构造函数
     */
    private static <T> Constructor<T> getMatchedConstructor(Constructor<T>[] constructors, Object[] args) {
        Constructor<T> mostSuitableConstructor = null;
        int length = Objects.nonNull(args) ? args.length : 0;
        Arrays.sort(constructors, Comparator.comparingInt(Constructor::getParameterCount));

        // 寻找参数个数为length 或 被@Primary注解标注的构造函数
        final List<Constructor<T>> temporaryCollection = Arrays.stream(constructors)
                .filter(constructor -> constructor.getParameterCount() == length ||
                        Objects.nonNull(constructor.getAnnotation(Primary.class)))
                .collect(LinkedList::new, List::add, List::addAll);

        // 寻找被@Primary注解标注的构造函数
        Constructor<T> primaryConstructor = temporaryCollection.stream()
                .filter(constructor -> Objects.nonNull(constructor.getAnnotation(Primary.class)))
                .findAny().orElse(null);

        // 如果存在被@Primary注解标注的构造函数且参数个数匹配，则将其移动到最前面
        if (!temporaryCollection.isEmpty() && Objects.nonNull(primaryConstructor)) {
            temporaryCollection.remove(primaryConstructor);

            if (primaryConstructor.getParameterCount() == length) {
                temporaryCollection.add(0, primaryConstructor);
            }
        }

        for (Constructor<T> constructor : temporaryCollection) {

            // 如果已经找到最合适的构造函数则跳出循环
            if (mostSuitableConstructor != null) {
                break;
            } else {
                // 根据参数类型寻找最合适的构造函数，如果找到则记录为最合适的构造函数
                Class<?>[] parameterTypes = constructor.getParameterTypes();

                // 遍历判断构造函数的参数 与 给定的参数是否匹配
                for (int index = 0; index < length; index++) {
                    Object parameter = args[index];
                    final Class<?> parameterType = parameterTypes[index];

                    // 如果 parameterType 是基本类型，且 parameter 为 null 或 与参数类型不匹配，则跳出循环
                    if (parameterType.isPrimitive()) {
                        if (Objects.isNull(parameter) || Objects.equals(parameter.getClass(), parameterType)) {
                            break;
                        }
                        // 如果 parameterType 不是基本类型，parameter 不为 null 且 与参数类型不匹配，则跳出循环
                    } else if (Objects.nonNull(parameter) && !parameterType.isAssignableFrom(parameter.getClass())) {
                        break;
                    }

                    // 如果所有参数都匹配，则记录为最合适的构造函数
                    if (index == length - 1) {
                        mostSuitableConstructor = constructor;
                    }
                }
            }
        }
        return mostSuitableConstructor;
    }

    /*-----------------------------------------------public static class----------------------------------------------*/

    public static class ConstructorConstant {

        /**
         * 空的 Object 类型数组对象
         */
        public static final Object[] EMPTY_OBJECT_ARRAY = {};

    }

}
